#include "head.h"

#define SIZE(a) (sizeof(a)/sizeof(a[0]))

int putsFile(int peerfd,char* para);
int getsFile(int peerfd,char* para);

typedef enum
{
    CMD_TYPE_CD = 1,
    CMD_TYPE_LS = 2,
    CMD_TYPE_PWD = 4,
    CMD_TYPE_PUT = 8,
    CMD_TYPE_GET = 16,
    CMD_TYPE_RM = 32,
    CMD_TYPE_MKDIR = 64
}CmdType;

typedef struct {
    int length;
    char buff[1024];
} train_t;

int recvCircle(int fd, char * buff, int length)
{
    char * pbuf = buff;
    int left = length;
    int ret = 0;
    while(left > 0) {
        ret = recv(fd, pbuf, left, 0);
        if(0 == ret) {
            close(fd);
            break;
        } else if(ret == -1 && errno == EINTR) {
            continue;
        } else if(ret == -1) {
            perror("recv");
            break;
        } else {// > 0
            pbuf += ret;
            left -= ret;
        }
        //printf("recv %f%%\r",(float)ret/left * 100);
    }
    return length - left;
}

int main(int argc, char *argv[])
{
    //0. 创建套接字
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    ERROR_CHECK(clientfd, -1, "socket");

    printf("sizeof(struct sockaddr):%ld\n", sizeof(struct sockaddr));
    printf("sizeof(struct sockaddr_in):%ld\n", sizeof(struct sockaddr_in));

    //1. 设置服务器的网络地址
    struct sockaddr_in serverAddr;
    memset(&serverAddr, 0, sizeof(serverAddr));

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(8888);
    serverAddr.sin_addr.s_addr = inet_addr("192.168.85.128");

    int ret = connect(clientfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
    ERROR_CHECK(ret, -1, "connect");
    printf("conn has connected\n");
        train_t namet;
        train_t keyt;
    //while(1){
    //    memset(&namet, 0, sizeof(namet));
    //    memset(&keyt, 0, sizeof(keyt));
    //    printf("请输入用户名：\n");
    //    char name[26] = {0};
    //    scanf("%s", name);
    //    namet.length = strlen(name);
    //    strcpy(namet.buff, name);
    //    send(clientfd, &namet, 4 + namet.length, MSG_WAITALL);
    //    printf("请输入密码：\n");
    //    char key[64] = {0};
    //    scanf("%s", key);
    //    keyt.length = strlen(key);
    //    strcpy(keyt.buff, key);
    //    send(clientfd, &keyt, 4 + keyt.length, MSG_WAITALL);
    //    char confirm[100] = {0};
    //    int confirmlen;
    //    recv(clientfd, &confirmlen, 4, MSG_WAITALL);
    //    recv(clientfd, confirm, confirmlen, MSG_WAITALL);
    //    if(strcmp(confirm, "登陆成功\n") == 0){
    //        printf("%s", confirm);
    //        break;
    //    }else{
    //        continue;
    //    }
    //}
    while(1)
    {
        printf("请输入指令:");
        fflush(stdout);

        char cmd[100] = {0};
        int i = 0;
        char ch = -1;
        while((ch = getchar()) != '\n')
        {
            cmd[i++] = ch;
        }

        char Cmd[10] = {0};
        int len = 0;
        while(cmd[len] && cmd[len] != ' ')
        {
            len++;
        }
        strncpy(Cmd,cmd,len);
        char para[100] = {0};
        strncpy(para,cmd + len + 1,100 - len);

        //先发四字节指令类型，再发100字节参数
        if(strcmp(Cmd,"ls") == 0)
        {
            int cmd = CMD_TYPE_LS;
            send(clientfd,&cmd,sizeof(cmd),MSG_WAITALL);
        }
        else if(strcmp(Cmd,"cd") == 0)
        {
            int cmd = CMD_TYPE_CD;
            send(clientfd,&cmd,sizeof(cmd),MSG_WAITALL);
        }
        else if(strcmp(Cmd,"pwd") == 0)
        {
            int cmd = CMD_TYPE_PWD;
            send(clientfd,&cmd,sizeof(cmd),MSG_WAITALL);
        }
        else if(strcmp(Cmd,"puts") == 0)
        {
            //上传下载文件需要执行对应函数进行
            int cmd = CMD_TYPE_PUT;
            send(clientfd,&cmd,sizeof(cmd),MSG_WAITALL);
            int len = putsFile(clientfd,para);
            //判断返回值，文件是否发送完成
            if(len != 0)
            {
                printf("puts error\n");
            }
            continue;
        }
        else if(strcmp(Cmd,"gets") == 0)
        {
            int cmd = CMD_TYPE_GET;
            send(clientfd,&cmd,sizeof(cmd),MSG_WAITALL);
            int len = getsFile(clientfd,para);
            if(len != 0)
            {
                printf("gets file error\n");
            }
            continue;
        }
        else if(strcmp(Cmd,"rm") == 0)
        {
            int cmd = CMD_TYPE_RM;
            send(clientfd,&cmd,sizeof(cmd),MSG_WAITALL);
        }
        else if(strcmp(Cmd,"mkdir") == 0)
        {
            int cmd = CMD_TYPE_MKDIR;
            send(clientfd,&cmd,sizeof(cmd),MSG_WAITALL);
        }
        else
        {
            printf("error cmd\n");
            continue;
        }
        send(clientfd,para,100,MSG_WAITALL);

        //展示结果

        char recvbuf[1024];
        memset(&recvbuf, 0, 1024);
        printf("开始接收结果\n");
        int recvlen = 0;
        recvCircle(clientfd,(char*)&recvlen,4);
        //recv(clientfd,recvbuf,100,0);
        recvCircle(clientfd,recvbuf,recvlen);
        puts(recvbuf);

    }
    close(clientfd);
}

int putsFile(int peerfd,char* para)
{
    //1. 读取本地文件
    int fd = open(para, O_RDWR);
    ERROR_CHECK(fd, -1, "open");

    //2. 先发文件名
    train_t train;
    memset(&train, 0, sizeof(train));
    train.length = strlen(para);//100
    strcpy(train.buff, para);
    int ret = send(peerfd, &train, 4 + train.length, MSG_WAITALL);//104

    //3. 再发文件内容
    //3.1 获取文件的长度
    struct stat st;
    fstat(fd, &st);
    printf("file's length: %ld\n", st.st_size);
    //printf("sizeof(st.st_size): %ld\n", sizeof(st.st_size));
    send(peerfd, &st.st_size, 8, MSG_WAITALL);
    ssize_t sendSize = 0;
    while(sendSize < st.st_size) {
        memset(&train, 0, sizeof(train));
        //从本地文件中读取,将其放入train
        ret = read(fd, train.buff, sizeof(train.buff)); 
        train.length = ret;
        //发送数据给到对端
        ret = send(peerfd, &train, 4 + train.length, MSG_WAITALL);
        if(ret > 0) {
            sendSize += (ret - 4);
        }else {
            perror("send");
            break;
        }
    }
    close(fd);
    return sendSize - st.st_size;
}

int getsFile(int peerfd,char* para)
{
    //1. 先发文件名，之后可以发md5值。
    train_t train;
    memset(&train, 0, sizeof(train));
    train.length = strlen(para);//100
    strcpy(train.buff, para);
    int ret = send(peerfd, &train, 4 + train.length, MSG_WAITALL);//104

    //2.在本地建立文件
    int fd = open(para,O_RDWR | O_CREAT | O_TRUNC,0664);
    ERROR_CHECK(fd,-1,"open");

    //3. 再接收文件内容
    //3.1 先接文件长度
    size_t filelength = 0;

    recvCircle(peerfd, (char*)&filelength, 8);
    printf("filecontent length: %ld\n", filelength);

    //3.2 再接文件内容
    int length = 0;
    size_t recvSize = 0;
    while(recvSize < filelength) {
        char buff[1024] = {0};
        recvCircle(peerfd, (char*)&length, 4);
        recvCircle(peerfd, buff, length);
        //3. 写入本地
        write(fd, buff, length);
        recvSize += length;
    }
    close(fd);
    return recvSize - filelength;
}
